/*
 * MegaMek -
 * Copyright (C) 2007 Ben Mazur (bmazur@sev.org)
 * 
 *  This program is free software; you can redistribute it and/or modify it
 *  under the terms of the GNU General Public License as published by the Free
 *  Software Foundation; either version 2 of the License, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 *  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 *  for more details.
 */
package megamek.client.bot;

import java.util.Comparator;

import megamek.common.AmmoType;
import megamek.common.Compute;
import megamek.common.Mounted;
import megamek.common.ToHitData;
import megamek.common.WeaponType;

// TODO: Auto-generated Javadoc
/**
 * The Class AttackOption.
 */
public class AttackOption extends ToHitData {

    /** The Constant serialVersionUID. */
    private static final long serialVersionUID = -8566472187475019360L;

    /**
     * The Class Sorter.
     */
    static class Sorter implements Comparator<AttackOption> {
        
        /** The primary. */
        transient CEntity primary = null;

        /**
         * Instantiates a new sorter.
         *
         * @param primary_target the primary_target
         */
        public Sorter(final CEntity primary_target) {
            primary = primary_target;
        }

        /* (non-Javadoc)
         * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
         */
        public int compare(final AttackOption attackOptionOne, final AttackOption attackOptionTwo) {
        	
            if (attackOptionOne.target.getKey().intValue() == attackOptionTwo.target.getKey().intValue()) {
            	final WeaponType weaponOne = (WeaponType) attackOptionOne.weapon.getType();
               	final WeaponType weaponTwo = (WeaponType) attackOptionTwo.weapon.getType();
                if (weaponOne.getDamage() == WeaponType.DAMAGE_MISSILE) {
                    if (weaponTwo.getDamage() == WeaponType.DAMAGE_MISSILE) {
                        if (attackOptionOne.expected > attackOptionTwo.expected) {
                            return -1;
                        }
                        return 1;
                    }
                    return 1;
                } else if (weaponOne.getDamage() == WeaponType.DAMAGE_MISSILE) {
                    return -1;
                } else if (attackOptionOne.expected > attackOptionTwo.expected) {
                    return -1;
                } else {
                    return 1;
                }
            } else if (attackOptionOne.target.getKey().equals(primary.getKey())) {
                return -1;
            }
            return 1;
        }
    }

    /** The target. */
    public transient CEntity target;
    
    /** The value. */
    public transient double value;
    
    /** The weapon. */
    public transient Mounted weapon;
    
    /** The to hit. */
    public transient ToHitData toHit;
    
    /** The odds. */
    public transient double odds; // secondary odds
    
    /** The primary_odds. */
    public transient double primaryOdds; // primary odds
    
    /** The heat. */
    public transient int heat;
    
    /** The expected. */
    public transient double expected; // damage adjusted by secondary to-hit odds
    
    /** The primary_expected. */
    public transient double primaryExpected; // damage adjusted by primary to-hit odds
    
    /** The ammo left. */
    public transient int ammoLeft = -1; // -1 doesn't use ammo
    
    /** The use_mode. */
    public transient String useMode = "None"; // The mode the weapon is set to for
    
    // this option

    // TODO: Add argument for the precise bin of ammo being used for this option
    // so it can be reloaded later
    /**
     * Instantiates a new attack option.
     *
     * @param target the target
     * @param weapon the weapon
     * @param value the value
     * @param toHit the to hit
     * @param sec_mod the sec_mod
     */
    public AttackOption(final CEntity target, final Mounted weapon, final double value,
            final ToHitData toHit, final int sec_mod) {
        this.target = target;
        this.weapon = weapon;
        this.toHit = toHit;
        this.value = value;

        if (target != null && weapon != null) {
            if (weapon.getType().getModesCount() > 0) {
                useMode = weapon.curMode().getName();
            }
            final WeaponType weaponT = (WeaponType) weapon.getType();

            // As a primary attack. Damage is already odds-adjusted.
            primaryOdds = Compute.oddsAbove(toHit.getValue()) / 100.0;
            primaryExpected = this.value;

            // As a secondary attack. Raw damage is extracted, then adjusted
            // for secondary to-hit odds. Since units with active Stealth armor
            // cannot be secondary targets, chances of hitting are 0.

            if (target.getEntity().isStealthActive()) {
                odds = 0.0;
            } else {
                odds = sec_mod <= 12 ? (Compute.oddsAbove(toHit.getValue()
                        + sec_mod) / 100.0) : 0.0;
            }
            heat = weaponT.getHeat();
            expected = this.value / primaryOdds;
            expected = expected * odds;

            // Check for ammo; note that some conventional infantry and BA
            // weapons do NOT return AmmoType.T_NA

            final boolean isInfantryWeapon = weaponT.hasFlag(WeaponType.F_INFANTRY);
            final boolean usesAmmo = (!isInfantryWeapon & weaponT.getAmmoType() != AmmoType.T_NA);

            final Mounted ammo = usesAmmo ? weapon.getLinked() : null;
            if (usesAmmo && (ammo == null || ammo.getShotsLeft() == 0)) {
                this.value = 0.0; // should have already been caught...
                primaryExpected = 0.0;
                expected = 0.0;
            } else if (usesAmmo) {
                ammoLeft = ammo.getShotsLeft();
            }
        }
    }
}